package web.dao;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.StringTokenizer;

import org.apache.log4j.Logger;

import common.LogCategory;

public class BaseDAO {

	protected static final Logger log = LogCategory.getCategory(BaseDAO.class);
	private Session session;

	protected Session getSession() {
		if (session == null) {
			session = new Session();
		}
		return session;
	}

	protected Collection<?> executeQuery(Class<?> persistentClass, String sql) {
		log.info("exec: " + sql);
		Collection<?> result = getSession().executeQuery(persistentClass, sql);
		return result;
	}

	protected int executeUpdate(String sql) {
		log.info("exec: " + sql);
		int result = getSession().executeUpdate(sql);
		return result;
	}

	/** extract the property of the class
	 * @return property descriptor
	 */
	public static PropertyDescriptor getProperty(Class<?> persistentClass, String property) {
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(persistentClass);
			PropertyDescriptor[] allPropertieDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor propertyDescriptor : allPropertieDescriptors) {
				if (propertyDescriptor.getName().equals(property)) {
					return propertyDescriptor;
				}
			}
			throw new RuntimeException("Can't find property " + property + " in class" + persistentClass + "!");
		} catch (IntrospectionException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/** extract properties 
	 * @return properties
	 */
	public static Collection<PropertyDescriptor> getProperties(Class<?> persistentClass) {
		return getProperties(persistentClass, null);
	}

	/** extract properties of the class
	 * @return properties
	 */
	public static Collection<PropertyDescriptor> getProperties(Class<?> persistentClass, Collection<String> excludePropertyNames) {
		try {
			Collection<PropertyDescriptor> propertyDescriptors = new LinkedHashSet<PropertyDescriptor>();
			BeanInfo beanInfo = Introspector.getBeanInfo(persistentClass);
			PropertyDescriptor[] allPropertieDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor propertyDescriptor : allPropertieDescriptors) {
				if (/** exclude properties if any */
						(((null != excludePropertyNames) && (!excludePropertyNames.contains(propertyDescriptor.getName()))) ||
						 (null == excludePropertyNames)) &&
						/** check properties */
						(null != propertyDescriptor.getReadMethod()) &&
						(Void.TYPE != propertyDescriptor.getReadMethod().getReturnType()) && 
						(0 == propertyDescriptor.getReadMethod().getParameterTypes().length) &&
						(null != propertyDescriptor.getWriteMethod()) &&
						(Void.TYPE == propertyDescriptor.getWriteMethod().getReturnType()) && 
						(1 == propertyDescriptor.getWriteMethod().getParameterTypes().length)) {
					propertyDescriptors.add(propertyDescriptor);
				}
			}
			return propertyDescriptors;
		} catch (IntrospectionException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/** extract property value of the bean
	 * @param property
	 * @param bean 
	 * @return value
	 */
	@SuppressWarnings("unchecked")
	public static <V> V getValue(PropertyDescriptor property, Object bean) {
		try {
			Method getter = property.getReadMethod();
			V value = (V) getter.invoke(bean, new Object[0]);
			if (value instanceof String) {
				value = (V) doubleQuotes((String)value);
			}
			return value;
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/** doubles the single quotes in text parameter */
	protected static String doubleQuotes(String value) {
		StringTokenizer tokens = new StringTokenizer(value, "'", true);
		StringBuffer result = new StringBuffer();
		while (tokens.hasMoreTokens()) {
			String token = tokens.nextToken();
			if (token.equals("'")) {
				result.append(token);
			}
			result.append(token);
		}
		return value;
	}

	/** extract property value of the bean
	 * @param property
	 * @param bean 
	 * @return value
	 */
	public static void setValue(PropertyDescriptor property, Object bean, Object value) {
		try {
			Method getter = property.getWriteMethod();
			getter.invoke(bean, new Object[]{value});
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}
}
